from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django_redis import get_redis_connection
from django.conf import settings
import random
from web import models
from utils.news126.email import send_email
from utils.safe import encrypt
from web.forms.bootstrap import BootStrapForm


# Create your views here.
class RegisterModelForm(BootStrapForm, forms.ModelForm):
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "密码长度不能小于8个字符",
            'max_length': "密码长度不能多于64个字符",
        },
        widget=forms.PasswordInput()
    )
    confirm_password = forms.CharField(
        label='确认密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "重复密码长度不能小于8个字符",
            'max_length': "重复密码长度不能多于64个字符",
        },
        widget=forms.PasswordInput()
    )
    email = forms.CharField(
        label='邮箱',
        validators=[RegexValidator(r'^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', '邮箱格式错误'), ]
    )
    code = forms.CharField(label='邮箱验证码')

    class Meta:
        model = models.UserInfo
        # fields = "__all__"
        fields = ['username', 'mobile_phone', 'password', 'confirm_password', 'email', 'code']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            # 这里name是数据库的字段名
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入{}'.format(field.label)

    def clean_username(self):
        username = self.cleaned_data['username']

        is_exist = models.UserInfo.objects.filter(username=username).exists()
        if is_exist:
            # self.add_error("username", "用户名已存在")
            raise ValidationError("用户名已存在")

        return username

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']

        is_exist = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if is_exist:
            raise ValidationError("手机号已存在")

        return mobile_phone

    def clean_password(self):
        password = self.cleaned_data['password']
        # 加密
        md5_password = encrypt.md5(password)

        return md5_password

    def clean_confirm_password(self):
        password = self.cleaned_data['password']  # 这里必须验证顺序在其之后才能拿到
        confirm_password = encrypt.md5(self.cleaned_data['confirm_password'])
        # 这里是让密文和密文进行比较
        if password != confirm_password:
            raise ValidationError("两次输入的密码不一致")

        return confirm_password

    def clean_email(self):
        email = self.cleaned_data['email']
        is_exist = models.UserInfo.objects.filter(email=email).exists()
        if is_exist:
            raise ValidationError("邮箱已注册")

        return email

    def clean_code(self):
        code = self.cleaned_data['code']

        # email = self.cleaned_data['email']  # 只写这行代码会有bug
        email = self.cleaned_data.get("email")
        if not email:
            return code

        # 从redis中取出验证码进行校验
        conn = get_redis_connection()
        redis_code = conn.get(email)
        if not redis_code:
            raise ValidationError("验证码失效或未发送，请重新发送")

        if redis_code.decode('utf-8') != code.strip():
            raise ValidationError("验证码错误，请重新输入")

        return code

class SendEmailForm(forms.Form):
    email = forms.CharField(
        label='邮箱',
        validators=[RegexValidator(r'^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', '邮箱格式错误'), ]
    )

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    # 钩子函数
    def clean_email(self):
        """ 对邮箱进行校验的钩子 """
        email = self.cleaned_data['email']

        # 判断短信模板是否有问题
        tpl = self.request.GET.get('tpl')
        template_id = settings.EMAIL_TEMPLATE.get(tpl)
        if not template_id:
            raise ValidationError("模板错误")

        # 校验数据库中是否已有邮箱
        is_exist = models.UserInfo.objects.filter(email=email).exists()
        if tpl == 'login':
            if not is_exist:
                raise ValidationError("用户不存在")
        else:
            if is_exist:
                raise ValidationError("注册邮箱已存在")

        # 发短信 & 写redis
        code = random.randrange(1000, 9999)
        res = send_email(email, tpl, code)

        if res['result'] != 0:
            raise ValidationError("邮件发送失败, {}".format(res['errmsg']))

        # 将验证码写入redis中
        # 去连接池中获取⼀个连接
        conn = get_redis_connection()  # 默认去default中（读写分离的时候考虑用多个）
        conn.set(email, code, ex=60)

        return email

class LoginEmailForm(BootStrapForm, forms.Form):
    # 用form的话所有的字段就得自己写了，不能用Meta了
    email = forms.CharField(
        label='邮箱',
        validators=[RegexValidator(r'^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', '邮箱格式错误'), ]
    )
    code = forms.CharField(label='邮箱验证码')

    def clean_email(self):
        email = self.cleaned_data['email']
        is_exist = models.UserInfo.objects.filter(email=email).exists()
        if not is_exist:
            raise ValidationError("邮箱不存在")

    def clean_code(self):
        code = self.cleaned_data['code']
        email = self.cleaned_data.get("email")
        if not email:
            return code

        # 从redis中取出验证码进行校验
        conn = get_redis_connection()
        redis_code = conn.get(email)
        if not redis_code:
            raise ValidationError("验证码失效或未发送，请重新发送")

        if redis_code.decode('utf-8') != code.strip():
            raise ValidationError("验证码错误，请重新输入")

        return code

class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label="用户名")
    password = forms.CharField(
        label='密码',
        widget=forms.PasswordInput()
    )
    code = forms.CharField(label="图片验证码")

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    def clean_code(self):
        """ 钩子 图片验证码是否正确？ """
        # 读取用户输入的验证码
        code = self.cleaned_data['code']

        # 去session获取自己的验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期，请重新获取')

        if code.strip().upper() != session_code.strip().upper():
            raise ValidationError('验证码输入错误')

        return code